"""
性能监控服务
"""

import asyncio
import time
import psutil
import threading
from typing import Dict, List, Any, Optional, Callable
from dataclasses import dataclass, field, asdict
from enum import Enum
from collections import deque, defaultdict
import statistics
import json
from datetime import datetime, timedelta

from ...core.exceptions import BusinessLogicError, ValidationError
from ...core.logging import get_logger
from ...core.config import settings

logger = get_logger(__name__)


class MetricType(Enum):
    """指标类型"""
    COUNTER = "counter"       # 计数器
    GAUGE = "gauge"          # 仪表盘
    HISTOGRAM = "histogram"  # 直方图
    TIMER = "timer"          # 计时器


class AlertLevel(Enum):
    """告警级别"""
    INFO = "info"
    WARNING = "warning"
    ERROR = "error"
    CRITICAL = "critical"


@dataclass
class Metric:
    """性能指标"""
    name: str
    value: float
    metric_type: MetricType
    labels: Dict[str, str] = field(default_factory=dict)
    timestamp: float = field(default_factory=time.time)
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        return {
            "name": self.name,
            "value": self.value,
            "type": self.metric_type.value,
            "labels": self.labels,
            "timestamp": self.timestamp
        }


@dataclass
class Alert:
    """告警"""
    id: str
    name: str
    level: AlertLevel
    message: str
    threshold: float
    current_value: float
    metric_name: str
    labels: Dict[str, str] = field(default_factory=dict)
    created_at: datetime = field(default_factory=datetime.utcnow)
    resolved_at: Optional[datetime] = None
    resolved: bool = False
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        return {
            "id": self.id,
            "name": self.name,
            "level": self.level.value,
            "message": self.message,
            "threshold": self.threshold,
            "current_value": self.current_value,
            "metric_name": self.metric_name,
            "labels": self.labels,
            "created_at": self.created_at.isoformat(),
            "resolved_at": self.resolved_at.isoformat() if self.resolved_at else None,
            "resolved": self.resolved
        }


@dataclass
class AlertRule:
    """告警规则"""
    id: str
    name: str
    metric_name: str
    threshold: float
    comparison: str  # >, <, >=, <=, ==, !=
    level: AlertLevel
    duration: float = 60.0  # 持续时间（秒）
    labels: Dict[str, str] = field(default_factory=dict)
    enabled: bool = True
    
    def evaluate(self, current_value: float) -> bool:
        """评估规则"""
        if not self.enabled:
            return False
        
        if self.comparison == ">":
            return current_value > self.threshold
        elif self.comparison == "<":
            return current_value < self.threshold
        elif self.comparison == ">=":
            return current_value >= self.threshold
        elif self.comparison == "<=":
            return current_value <= self.threshold
        elif self.comparison == "==":
            return abs(current_value - self.threshold) < 0.0001
        elif self.comparison == "!=":
            return abs(current_value - self.threshold) >= 0.0001
        
        return False


class MetricsCollector:
    """指标收集器"""
    
    def __init__(self):
        self.metrics: Dict[str, List[Metric]] = defaultdict(list)
        self.counters: Dict[str, float] = defaultdict(float)
        self.gauges: Dict[str, float] = defaultdict(float)
        self.histograms: Dict[str, deque] = defaultdict(lambda: deque(maxlen=1000))
        self.timers: Dict[str, deque] = defaultdict(lambda: deque(maxlen=1000))
        
        # 配置
        self.max_metrics_per_name = 1000
        self.cleanup_interval = 300  # 5分钟
        
        # 回调函数
        self.metric_callbacks: List[Callable[[Metric], None]] = []
        
        # 启动清理任务
        self._cleanup_task = None
        self.start_cleanup_task()
    
    def start_cleanup_task(self):
        """启动清理任务"""
        if self._cleanup_task is None:
            self._cleanup_task = asyncio.create_task(self._cleanup_loop())
    
    def stop_cleanup_task(self):
        """停止清理任务"""
        if self._cleanup_task:
            self._cleanup_task.cancel()
            self._cleanup_task = None
    
    def increment_counter(self, name: str, value: float = 1.0, labels: Dict[str, str] = None):
        """递增计数器"""
        self.counters[name] += value
        
        metric = Metric(
            name=name,
            value=self.counters[name],
            metric_type=MetricType.COUNTER,
            labels=labels or {}
        )
        
        self._add_metric(metric)
    
    def set_gauge(self, name: str, value: float, labels: Dict[str, str] = None):
        """设置仪表盘值"""
        self.gauges[name] = value
        
        metric = Metric(
            name=name,
            value=value,
            metric_type=MetricType.GAUGE,
            labels=labels or {}
        )
        
        self._add_metric(metric)
    
    def add_histogram_value(self, name: str, value: float, labels: Dict[str, str] = None):
        """添加直方图值"""
        self.histograms[name].append(value)
        
        metric = Metric(
            name=name,
            value=value,
            metric_type=MetricType.HISTOGRAM,
            labels=labels or {}
        )
        
        self._add_metric(metric)
    
    def record_timer(self, name: str, duration: float, labels: Dict[str, str] = None):
        """记录计时器"""
        self.timers[name].append(duration)
        
        metric = Metric(
            name=name,
            value=duration,
            metric_type=MetricType.TIMER,
            labels=labels or {}
        )
        
        self._add_metric(metric)
    
    def get_counter(self, name: str) -> float:
        """获取计数器值"""
        return self.counters.get(name, 0.0)
    
    def get_gauge(self, name: str) -> float:
        """获取仪表盘值"""
        return self.gauges.get(name, 0.0)
    
    def get_histogram_stats(self, name: str) -> Dict[str, float]:
        """获取直方图统计"""
        if name not in self.histograms or not self.histograms[name]:
            return {}
        
        values = list(self.histograms[name])
        return {
            "count": len(values),
            "sum": sum(values),
            "min": min(values),
            "max": max(values),
            "mean": statistics.mean(values),
            "median": statistics.median(values),
            "p95": self._percentile(values, 0.95),
            "p99": self._percentile(values, 0.99)
        }
    
    def get_timer_stats(self, name: str) -> Dict[str, float]:
        """获取计时器统计"""
        if name not in self.timers or not self.timers[name]:
            return {}
        
        durations = list(self.timers[name])
        return {
            "count": len(durations),
            "sum": sum(durations),
            "min": min(durations),
            "max": max(durations),
            "mean": statistics.mean(durations),
            "median": statistics.median(durations),
            "p95": self._percentile(durations, 0.95),
            "p99": self._percentile(durations, 0.99)
        }
    
    def get_metrics_history(self, name: str, limit: int = 100) -> List[Metric]:
        """获取指标历史"""
        history = self.metrics.get(name, [])
        return history[-limit:]
    
    def get_all_metrics(self) -> Dict[str, List[Metric]]:
        """获取所有指标"""
        return dict(self.metrics)
    
    def add_metric_callback(self, callback: Callable[[Metric], None]):
        """添加指标回调"""
        self.metric_callbacks.append(callback)
    
    def _add_metric(self, metric: Metric):
        """添加指标"""
        # 限制历史记录数量
        if len(self.metrics[metric.name]) >= self.max_metrics_per_name:
            self.metrics[metric.name].pop(0)
        
        self.metrics[metric.name].append(metric)
        
        # 通知回调
        for callback in self.metric_callbacks:
            try:
                callback(metric)
            except Exception as e:
                logger.error(f"Metric callback error: {e}")
    
    def _percentile(self, values: List[float], percentile: float) -> float:
        """计算百分位数"""
        if not values:
            return 0.0
        
        sorted_values = sorted(values)
        index = int(len(sorted_values) * percentile)
        return sorted_values[min(index, len(sorted_values) - 1)]
    
    async def _cleanup_loop(self):
        """清理循环"""
        while True:
            try:
                await asyncio.sleep(self.cleanup_interval)
                await self._cleanup_old_metrics()
            except asyncio.CancelledError:
                break
            except Exception as e:
                logger.error(f"Metrics cleanup error: {e}")
                await asyncio.sleep(60)
    
    async def _cleanup_old_metrics(self):
        """清理旧指标"""
        cutoff_time = time.time() - 3600  # 1小时前的指标
        
        for name, metrics in self.metrics.items():
            # 移除旧指标
            self.metrics[name] = [
                metric for metric in metrics 
                if metric.timestamp > cutoff_time
            ]
        
        # 清理直方图和计时器中的旧数据
        for name in list(self.histograms.keys()):
            while (self.histograms[name] and 
                   time.time() - time.time() > 3600):
                self.histograms[name].popleft()
        
        for name in list(self.timers.keys()):
            while (self.timers[name] and 
                   time.time() - time.time() > 3600):
                self.timers[name].popleft()


class SystemMonitor:
    """系统监控"""
    
    def __init__(self, metrics_collector: MetricsCollector):
        self.metrics_collector = metrics_collector
        self.monitoring = False
        self.monitor_task = None
        self.monitor_interval = 10  # 10秒
        
        # 进程信息
        self.process = psutil.Process()
    
    async def start_monitoring(self):
        """开始监控"""
        if self.monitoring:
            return
        
        self.monitoring = True
        self.monitor_task = asyncio.create_task(self._monitor_loop())
        
        logger.info("System monitoring started")
    
    async def stop_monitoring(self):
        """停止监控"""
        if not self.monitoring:
            return
        
        self.monitoring = False
        
        if self.monitor_task:
            self.monitor_task.cancel()
            try:
                await self.monitor_task
            except asyncio.CancelledError:
                pass
        
        logger.info("System monitoring stopped")
    
    async def _monitor_loop(self):
        """监控循环"""
        while self.monitoring:
            try:
                await self._collect_system_metrics()
                await asyncio.sleep(self.monitor_interval)
            except asyncio.CancelledError:
                break
            except Exception as e:
                logger.error(f"System monitoring error: {e}")
                await asyncio.sleep(5)
    
    async def _collect_system_metrics(self):
        """收集系统指标"""
        try:
            # CPU使用率
            cpu_percent = self.process.cpu_percent()
            self.metrics_collector.set_gauge("process_cpu_percent", cpu_percent)
            
            # 系统CPU使用率
            system_cpu = psutil.cpu_percent()
            self.metrics_collector.set_gauge("system_cpu_percent", system_cpu)
            
            # 内存使用
            memory_info = self.process.memory_info()
            memory_percent = self.process.memory_percent()
            
            self.metrics_collector.set_gauge("process_memory_rss", memory_info.rss)
            self.metrics_collector.set_gauge("process_memory_vms", memory_info.vms)
            self.metrics_collector.set_gauge("process_memory_percent", memory_percent)
            
            # 系统内存
            system_memory = psutil.virtual_memory()
            self.metrics_collector.set_gauge("system_memory_total", system_memory.total)
            self.metrics_collector.set_gauge("system_memory_used", system_memory.used)
            self.metrics_collector.set_gauge("system_memory_percent", system_memory.percent)
            
            # 磁盘使用
            disk_usage = psutil.disk_usage('/')
            self.metrics_collector.set_gauge("disk_total", disk_usage.total)
            self.metrics_collector.set_gauge("disk_used", disk_usage.used)
            self.metrics_collector.set_gauge("disk_percent", disk_usage.percent)
            
            # 网络IO
            net_io = psutil.net_io_counters()
            self.metrics_collector.set_gauge("network_bytes_sent", net_io.bytes_sent)
            self.metrics_collector.set_gauge("network_bytes_recv", net_io.bytes_recv)
            
            # 进程信息
            num_threads = self.process.num_threads()
            self.metrics_collector.set_gauge("process_threads", num_threads)
            
            # 文件描述符
            if hasattr(self.process, 'num_fds'):
                num_fds = self.process.num_fds()
                self.metrics_collector.set_gauge("process_fds", num_fds)
            
            # 系统负载
            if hasattr(psutil, 'getloadavg'):
                load_avg = psutil.getloadavg()
                self.metrics_collector.set_gauge("system_load_1m", load_avg[0])
                self.metrics_collector.set_gauge("system_load_5m", load_avg[1])
                self.metrics_collector.set_gauge("system_load_15m", load_avg[2])
            
        except Exception as e:
            logger.error(f"Failed to collect system metrics: {e}")


class AlertManager:
    """告警管理器"""
    
    def __init__(self, metrics_collector: MetricsCollector):
        self.metrics_collector = metrics_collector
        self.alert_rules: Dict[str, AlertRule] = {}
        self.active_alerts: Dict[str, Alert] = {}
        self.alert_history: List[Alert] = []
        
        # 配置
        self.evaluation_interval = 30  # 30秒
        self.max_history_size = 1000
        
        # 控制
        self.running = False
        self.evaluation_task = None
        
        # 回调函数
        self.alert_callbacks: List[Callable[[Alert], None]] = []
    
    async def start(self):
        """启动告警管理器"""
        if self.running:
            return
        
        self.running = True
        self.evaluation_task = asyncio.create_task(self._evaluation_loop())
        
        # 添加默认规则
        self._add_default_rules()
        
        logger.info("Alert manager started")
    
    async def stop(self):
        """停止告警管理器"""
        if not self.running:
            return
        
        self.running = False
        
        if self.evaluation_task:
            self.evaluation_task.cancel()
            try:
                await self.evaluation_task
            except asyncio.CancelledError:
                pass
        
        logger.info("Alert manager stopped")
    
    def add_rule(self, rule: AlertRule):
        """添加告警规则"""
        self.alert_rules[rule.id] = rule
        logger.info(f"Added alert rule: {rule.name}")
    
    def remove_rule(self, rule_id: str) -> bool:
        """移除告警规则"""
        if rule_id in self.alert_rules:
            del self.alert_rules[rule_id]
            logger.info(f"Removed alert rule: {rule_id}")
            return True
        return False
    
    def get_active_alerts(self) -> List[Alert]:
        """获取活跃告警"""
        return list(self.active_alerts.values())
    
    def get_alert_history(self, limit: int = 100) -> List[Alert]:
        """获取告警历史"""
        return self.alert_history[-limit:]
    
    def acknowledge_alert(self, alert_id: str) -> bool:
        """确认告警"""
        if alert_id in self.active_alerts:
            alert = self.active_alerts[alert_id]
            # 这里可以添加确认逻辑
            logger.info(f"Acknowledged alert: {alert_id}")
            return True
        return False
    
    def resolve_alert(self, alert_id: str) -> bool:
        """解决告警"""
        if alert_id in self.active_alerts:
            alert = self.active_alerts[alert_id]
            alert.resolved = True
            alert.resolved_at = datetime.utcnow()
            
            # 移到历史记录
            del self.active_alerts[alert_id]
            self.alert_history.append(alert)
            
            # 限制历史记录大小
            if len(self.alert_history) > self.max_history_size:
                self.alert_history.pop(0)
            
            logger.info(f"Resolved alert: {alert_id}")
            return True
        return False
    
    def add_alert_callback(self, callback: Callable[[Alert], None]):
        """添加告警回调"""
        self.alert_callbacks.append(callback)
    
    async def _evaluation_loop(self):
        """评估循环"""
        while self.running:
            try:
                await self._evaluate_rules()
                await asyncio.sleep(self.evaluation_interval)
            except asyncio.CancelledError:
                break
            except Exception as e:
                logger.error(f"Alert evaluation error: {e}")
                await asyncio.sleep(10)
    
    async def _evaluate_rules(self):
        """评估告警规则"""
        for rule in self.alert_rules.values():
            if not rule.enabled:
                continue
            
            try:
                # 获取当前指标值
                current_value = self._get_metric_value(rule.metric_name)
                if current_value is None:
                    continue
                
                # 评估规则
                if rule.evaluate(current_value):
                    # 规则触发
                    await self._handle_alert_firing(rule, current_value)
                else:
                    # 规则未触发
                    await self._handle_alert_recovery(rule)
                
            except Exception as e:
                logger.error(f"Error evaluating rule {rule.id}: {e}")
    
    def _get_metric_value(self, metric_name: str) -> Optional[float]:
        """获取指标值"""
        # 先尝试从计数器和仪表盘获取
        if metric_name in self.metrics_collector.counters:
            return self.metrics_collector.counters[metric_name]
        
        if metric_name in self.metrics_collector.gauges:
            return self.metrics_collector.gauges[metric_name]
        
        # 从最新指标获取
        metrics = self.metrics_collector.get_metrics_history(metric_name, 1)
        if metrics:
            return metrics[-1].value
        
        return None
    
    async def _handle_alert_firing(self, rule: AlertRule, current_value: float):
        """处理告警触发"""
        alert_id = f"{rule.id}_{datetime.utcnow().strftime('%Y%m%d%H%M%S')}"
        
        if alert_id not in self.active_alerts:
            # 创建新告警
            alert = Alert(
                id=alert_id,
                name=rule.name,
                level=rule.level,
                message=f"{rule.name}: {rule.metric_name} {rule.comparison} {rule.threshold}",
                threshold=rule.threshold,
                current_value=current_value,
                metric_name=rule.metric_name,
                labels=rule.labels
            )
            
            self.active_alerts[alert_id] = alert
            
            # 通知回调
            await self._notify_alert(alert)
            
            logger.warning(f"Alert fired: {alert.name} - {alert.message}")
    
    async def _handle_alert_recovery(self, rule: AlertRule):
        """处理告警恢复"""
        # 查找该规则的活跃告警
        alerts_to_resolve = [
            alert for alert in self.active_alerts.values()
            if alert.metric_name == rule.metric_name
        ]
        
        for alert in alerts_to_resolve:
            await self._resolve_alert(alert.id)
    
    async def _notify_alert(self, alert: Alert):
        """通知告警"""
        for callback in self.alert_callbacks:
            try:
                if asyncio.iscoroutinefunction(callback):
                    await callback(alert)
                else:
                    callback(alert)
            except Exception as e:
                logger.error(f"Alert callback error: {e}")
    
    def _add_default_rules(self):
        """添加默认告警规则"""
        default_rules = [
            AlertRule(
                id="cpu_high",
                name="High CPU Usage",
                metric_name="process_cpu_percent",
                threshold=80.0,
                comparison=">",
                level=AlertLevel.WARNING,
                duration=60.0
            ),
            AlertRule(
                id="memory_high",
                name="High Memory Usage",
                metric_name="process_memory_percent",
                threshold=80.0,
                comparison=">",
                level=AlertLevel.WARNING,
                duration=60.0
            ),
            AlertRule(
                id="disk_high",
                name="High Disk Usage",
                metric_name="disk_percent",
                threshold=90.0,
                comparison=">",
                level=AlertLevel.ERROR,
                duration=30.0
            ),
            AlertRule(
                id="load_high",
                name="High System Load",
                metric_name="system_load_1m",
                threshold=2.0,
                comparison=">",
                level=AlertLevel.WARNING,
                duration=60.0
            )
        ]
        
        for rule in default_rules:
            self.add_rule(rule)


class PerformanceMonitor:
    """性能监控器"""
    
    def __init__(self):
        self.metrics_collector = MetricsCollector()
        self.system_monitor = SystemMonitor(self.metrics_collector)
        self.alert_manager = AlertManager(self.metrics_collector)
        
        # 控制状态
        self.running = False
        
        # 统计信息
        self.start_time = time.time()
    
    async def start(self):
        """启动性能监控"""
        if self.running:
            return
        
        self.running = True
        
        # 启动各个组件
        await self.system_monitor.start_monitoring()
        await self.alert_manager.start()
        
        logger.info("Performance monitor started")
    
    async def stop(self):
        """停止性能监控"""
        if not self.running:
            return
        
        self.running = False
        
        # 停止各个组件
        await self.system_monitor.stop_monitoring()
        await self.alert_manager.stop()
        self.metrics_collector.stop_cleanup_task()
        
        logger.info("Performance monitor stopped")
    
    def get_metrics_collector(self) -> MetricsCollector:
        """获取指标收集器"""
        return self.metrics_collector
    
    def get_system_monitor(self) -> SystemMonitor:
        """获取系统监控"""
        return self.system_monitor
    
    def get_alert_manager(self) -> AlertManager:
        """获取告警管理器"""
        return self.alert_manager
    
    async def get_performance_summary(self) -> Dict[str, Any]:
        """获取性能摘要"""
        uptime = time.time() - self.start_time
        
        # 获取关键指标
        cpu_percent = self.metrics_collector.get_gauge("process_cpu_percent")
        memory_percent = self.metrics_collector.get_gauge("process_memory_percent")
        system_cpu = self.metrics_collector.get_gauge("system_cpu_percent")
        system_memory = self.metrics_collector.get_gauge("system_memory_percent")
        disk_percent = self.metrics_collector.get_gauge("disk_percent")
        
        # 获取告警状态
        active_alerts = self.alert_manager.get_active_alerts()
        
        return {
            "uptime_seconds": uptime,
            "uptime_formatted": self._format_duration(uptime),
            "system_metrics": {
                "cpu_percent": system_cpu,
                "memory_percent": system_memory,
                "disk_percent": disk_percent
            },
            "process_metrics": {
                "cpu_percent": cpu_percent,
                "memory_percent": memory_percent
            },
            "alerts": {
                "active_count": len(active_alerts),
                "critical_count": len([a for a in active_alerts if a.level == AlertLevel.CRITICAL]),
                "error_count": len([a for a in active_alerts if a.level == AlertLevel.ERROR]),
                "warning_count": len([a for a in active_alerts if a.level == AlertLevel.WARNING])
            },
            "monitoring": {
                "running": self.running,
                "metrics_count": len(self.metrics_collector.metrics),
                "rules_count": len(self.alert_manager.alert_rules)
            }
        }
    
    def _format_duration(self, seconds: float) -> str:
        """格式化时长"""
        days = int(seconds // 86400)
        hours = int((seconds % 86400) // 3600)
        minutes = int((seconds % 3600) // 60)
        secs = int(seconds % 60)
        
        if days > 0:
            return f"{days}d {hours}h {minutes}m {secs}s"
        elif hours > 0:
            return f"{hours}h {minutes}m {secs}s"
        elif minutes > 0:
            return f"{minutes}m {secs}s"
        else:
            return f"{secs}s"


# 全局性能监控器实例
performance_monitor = PerformanceMonitor()
